﻿using System.Drawing;
using System.Windows.Forms;
using System;

namespace Library.Winform.Draw.Tools {
	public sealed class ToolPointer : ToolGraphic {
		public readonly static ToolPointer Pointer = new ToolPointer();
		private ToolPointer() {
			_start = Point.Empty;
			_last = Point.Empty;
			FrameColor = Color.Black;
			FrameStyle = FrameStyle.Dashed;
		}

		private enum SelectionMode {
			None,
			NetSelection,   // group selection is active
			Move,           // object(s) are moves
			ReSize            // object is resized
		}

		private SelectionMode _mode;
		private ObjectDraw _resizeObject;
		private UInt16 _reSizeObjecHandlePoint;
		private Point _last;
		private Point _start;

		public Color FrameColor { get; set; }
		public FrameStyle FrameStyle { get; set; }


		protected override void OnMouseDown(IDrawContainer container, ScrollMouseEventArgs e) {
			_mode = SelectionMode.None;
			var point = new Point(e.X, e.Y);
			ObjectDraw moveObject = null;
			// check for resize or move
			for (int i = 0; i < container.GraphicsList.Count; i++) {
				var drawObject = container.GraphicsList[i];
				if (!drawObject.Enable || !drawObject.Visible) continue;

				var hitHandle = drawObject.HitTest(point);
				if (hitHandle > 0) {
					// test for resize hight priority
					if (drawObject.Selected) {
						//ready for resize
						_mode = SelectionMode.ReSize;
						_resizeObject = drawObject;
						container.SelectAll(false);
						_resizeObject.Selected = true;
						_reSizeObjecHandlePoint = (ushort)hitHandle;
						break;
					}
				} else if (hitHandle == 0) {
					_mode = SelectionMode.Move;
					if (moveObject == null)
						moveObject = drawObject;
					//break;
				}
			}
			if (_mode == SelectionMode.Move && moveObject != null) {
				// Unselect all if Ctrl is not pressed and clicked object is not selected yet
				if ((Control.ModifierKeys & Keys.Control) == 0 && !moveObject.Selected)
					container.SelectAll(false);
				// Select clicked object
				moveObject.Selected = true;
				container.Cursor = Cursors.SizeAll;
			} else
				// Net selection
				if (_mode == SelectionMode.None) {
					// click on background
					if ((Control.ModifierKeys & Keys.Control) == 0)
						container.SelectAll(false);

					_mode = SelectionMode.NetSelection;
				}

			_last.X = e.X;
			_last.Y = e.Y;
			_start.X = e.X;
			_start.Y = e.Y;
			container.Capture = true;

			container.Refresh();

			if (_mode == SelectionMode.NetSelection) {
				// Draw selection rectangle in initial position

				ControlPaint.DrawReversibleFrame(container.RectangleToScreen(RectangleDraw.NormalizedRectangle(_start, _last)), FrameColor, FrameStyle);
			}
		}

		protected override void OnMouseMove(IDrawContainer container, ScrollMouseEventArgs e) {
			var point = new Point(e.X, e.Y);
			Point oldPoint = _last;

			// set cursor when mouse button is not pressed
			if (e.Button == MouseButtons.None) {
				Cursor cursor = null;

				for (int i = 0; i < container.GraphicsList.Count; i++) {
					var draw = container.GraphicsList[i];
					var n = draw.HitTest(point);

					if (n > 0) {
						cursor = draw.GetHandleCursor((UInt16)n);
						break;
					}
				}

				if (cursor == null)
					cursor = Cursors.Default;

				container.Cursor = cursor;

				return;
			}

			if (e.Button != MouseButtons.Left)
				return;

			// Left button is pressed

			// Find difference between previous and current position
			int dx = e.X - _last.X;
			int dy = e.Y - _last.Y;

			_last.X = e.X;
			_last.Y = e.Y;

			// resize
			if (_mode == SelectionMode.ReSize) {
				if (_resizeObject != null) {
					_resizeObject.MovePointHandleTo(_reSizeObjecHandlePoint, point);
					container.Refresh();
				}
			}

			// move
			if (_mode == SelectionMode.Move) {
				foreach (IObjectDraw o in container.GetSelected()) {
					o.Move(dx, dy);
				}

				container.Cursor = Cursors.SizeAll;
				//container.SetDirty();
				container.Refresh();
			}

			if (_mode == SelectionMode.NetSelection) {
				// Remove old selection rectangle
				var rec = container.RectangleToScreen(RectangleDraw.NormalizedRectangle(_start, oldPoint));
				ControlPaint.DrawReversibleFrame(rec, FrameColor, FrameStyle);
				// Draw new selection rectangle
				rec = container.RectangleToScreen(RectangleDraw.NormalizedRectangle(_start, point));
				ControlPaint.DrawReversibleFrame(rec, FrameColor, FrameStyle);
			}

		}

		protected override void OnMouseUp(IDrawContainer container, ScrollMouseEventArgs e) {
			if (_mode == SelectionMode.NetSelection) {
				// Remove old selection rectangle
				var normalRec = RectangleDraw.NormalizedRectangle(_start, _last);
				ControlPaint.DrawReversibleFrame(container.RectangleToScreen(normalRec), FrameColor, FrameStyle);
				// Make group selection
				container.SelectInRectangle(normalRec);
				_mode = SelectionMode.None;
			}

			if (_resizeObject != null) {
				// after resizing
				_resizeObject.Normalize();
				_resizeObject.OnResizeCompleted(EventArgs.Empty);
				_resizeObject = null;
			}
			if (_mode == SelectionMode.Move) {
				//after moving
				foreach (ObjectDraw o in container.GetSelected()) {
					o.OnMoveCompleted(EventArgs.Empty);
				}
			}

			container.Capture = false;
			container.Refresh();

			//if (commandChangeState != null && wasMove) {
			//  // Keep state after moving/resizing and add command to history
			//  commandChangeState.NewState(drawArea.GraphicsList);
			//  drawArea.AddCommandToHistory(commandChangeState);
			//  commandChangeState = null;
			//}
		}

		protected override void OnMouseClick(IDrawContainer container, ScrollMouseEventArgs e) {
			ObjectDraw draw = FindSelectOne(container);
			if (draw != null) {
				draw.FireClickEvent(e);
			}
		}

		public override void OnDoubleClick(IDrawContainer container, EventArgs e) {
			ObjectDraw draw = FindSelectOne(container);
			if (draw != null) {
				draw.FireDoubleClickEvent(e);
			}
		}

		protected override void OnMouseDoubleClick(IDrawContainer container, ScrollMouseEventArgs e) {
			ObjectDraw draw = FindSelectOne(container);
			if (draw != null) {
				draw.FireMouseDoubleClickEvent(e);
			}
		}

		private static ObjectDraw FindSelectOne(IDrawContainer container) {
			ObjectDraw draw = null;
			foreach (var objectDraw in container.GetSelected()) {

				if (draw != null) {
					//do not fire mouse click event when multy selection
					return null;
				}
				draw = objectDraw;
			}

			return draw;
		}
	}
}
